home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_n_z.arj / TSRSH.ASM < prev    next >
Assembly Source File  |  1987-02-02  |  27KB  |  1,100 lines

  1. PAGE    60,132
  2. TITLE    Ram-Resident Program Shell, Preliminary Version (Lesson 2)
  3. SUBTTL    Date 02-02-87 -- Tutorial for Microsoft Forum  Version 0.03
  4. ;-----------------------------------------------------------------------------
  5. ;    Ram-Resident Program Shell
  6. ;
  7. ;
  8. ;The software, documentation and source code are: 
  9. ;    (C) Copyright 1986, 1987
  10. ;    Chip Rabinowitz
  11. ;    All Rights Reserved 
  12. ;    51 East Rogues Path
  13. ;    Huntington Station NY 11746
  14. ;    Assistant SysOp, Microsoft Forum, Compuserve PPN 76703,350
  15. ;COPYRIGHT NOTICE AND WARRANTY INFORMATION 
  16. ;----------------------------------------- 
  17. ;This document ("the source code"), other accompanying written and disk-based
  18. ;notes and specifications ("the documentation"), and all referenced and related
  19. ;program files accompanying the source code and the documentation ("the
  20. ;software") are copyrighted by Chip Rabinowitz.  
  21. ;
  22. ;This program has been uploaded to Compuserve as part of a Tutorial in how
  23. ;to write Terminate-But-Stay-Resident programs, being conducted on
  24. ;Compuserve's Microsoft Special Interest Group during early 1987/
  25. ;
  26. ;This code (and all other code uploaded by the Copyright Holder as a 
  27. ;part of this TSR Tutorial) may be used with the following restrictions: 
  28. ;
  29. ;(1) If all or part of this code is used as part of a software package of 
  30. ;ANY kind, the following acknowledgement must be used in the Documentation 
  31. ;accompanying said package:
  32. ;
  33. ;    'Parts of the Resident Program Management for this Product are'
  34. ;    'Copyright (C) 1986, 1987 by Chip Rabinowitz, and are incorporated'
  35. ;    'into this product courtesy of the Ringmaster Development Team'
  36. ;
  37. ;(2) If all or part of this code is used as part of a software package that
  38. ;is placed in the public domain, no further restrictions apply.
  39. ;
  40. ;(3) If all or part of this code is used as part of a software package that
  41. ;is being distributed as 'shareware', a one-time-only donation of $10 will be
  42. ;accepted for the express purpose of continuing research into TSR standards.
  43. ;Note that this is a voluntary contribution, and should be sent to the
  44. ;Ringmaster Development Team at the address printed above.
  45. ;
  46. ;(4) If all or part of this code is used as part of a software package that
  47. ;will be distributed as a commercial product, a one-time-only payment of
  48. ;$25 will be accepted the express purpose of continuing research into TSR 
  49. ;standards.  Note that in THIS CASE ONLY, this is not a voluntary 
  50. ;contribution.  Commercial Developers are REQUIRED to make payment prior to
  51. ;incorporating these routines into their product.
  52. ;
  53. ;No copy of the source code may be distributed or given away without the
  54. ;accompanying documentation; and this notice must not be removed. 
  55. ;There is no warranty of any kind associated with this software, and the
  56. ;copyright owner is not liable for damages of any kind.  By using this
  57. ;software, you agree to this. 
  58. ;
  59.  
  60. XON     equ    11h
  61. XOFF    equ    13h
  62. TRUE    equ    1
  63. FALSE   equ    0
  64. CR    equ    0dh
  65. LF    equ    0ah
  66. TAB    equ    9
  67.  
  68.  
  69. HOTKEYON    equ    01h    ;hot key pressed
  70. SHIFTSON    equ    02h    ;shift states match
  71. TSRACTIVE    equ    04h    ;tsr is running in foreground
  72. INT28ACTIVE    equ    08h    ;INT28 routine is running in background
  73.  
  74. KB_DATA    equ    60h
  75. KB_CTL    equ    61h
  76. KB_FLG1    equ    17h
  77. KB_FLG2    equ    18h
  78. BIOS_DATA    equ    40h
  79.  
  80. code segment para public 'CODE'
  81.     org 100h
  82.     assume    cs:code, ds:code, es:code
  83. start:    jmp    begin
  84.  
  85.     db    1bh,'[2J'
  86. cpyrt    db    'Ram-Resident Program Shell, Preliminary Version (Lesson 2)',
  87.     db    CR,LF,LF
  88.     db    'Copyright (C) 1986, 1987 Chip Rabinowitz',
  89.     db    CR,LF,'All Rights Reserved',CR,LF,LF,'$'
  90. hello    db    'Hello World!',CR,LF,'$'
  91. bad_dos_msg    db    'Incompatible DOS version .... Aborting!',07h,CR,LF,'$'
  92.  
  93. tsr_parms:
  94.     PSP    dw    ?    ;PSP of TSR
  95.     OLDPSP    dw    ?    ;location to save current PSP
  96.     DTA    dd    ?    ;DTA of TSR
  97.     OLDDTA    dd    ?    ;location to save current DTA
  98.     DSEG    dw    ?    ;TSR's Data Segment
  99.     SSEG    dw    ?    ;TSR's Stack Segment ....
  100.     SPTR    dw    0fffeh    ;.... and Stack Pointer
  101.     ISSEG    dw    0    ;interrupted SS ...
  102.     ISPTR    dw    0    ;..... and SP
  103.     SHIFTST    db    0    ;Shift State to Activate
  104.     HOTKEY    db    0ffh    ;scan code to activate
  105.     STATUS    dw    0    ;current status words
  106.     POPUP    dd    0    ;far pointer to popup routine
  107.  
  108. intflags    db    0
  109. ININT13        equ    04h    ;Bios disk interrupt
  110. ININT21        equ    08h    ;DOS interrupt
  111.  
  112. dosversion    db    0    ;current version of DOS
  113. waitcount    db    0    ;wait to activate count
  114.  
  115. indosflag    dd    0    ;Pointer to INDOS flag
  116. doscriterr    dd    0    ;Pointer to DOS Critical Error Flag
  117.  
  118. dossseg        dw    0    ;pointer to dos stack segment
  119. dossptr        dw    0    ;pointer to dos stack pointer
  120. dossize        dw    0    ;dos stack size
  121.  
  122. dos21funcs    label    byte
  123.     db    0,    0,    0,    0,    0,    0,    0,    0    ;0-7
  124.     db    0,    0,    0,    0,    0,    0ffh,    0ffh,    0ffh    ;8-f
  125.     db     0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;10-17
  126.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;18-1f
  127.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0,    0ffh    ;20-27
  128.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0    ;28-2f
  129.     db    0ffh,    0,    0,    0,    0,    0,    0ffh,    0ffh    ;30-37
  130.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;38-3f
  131.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;40-47
  132.     db    0,    0,    0,    0,    0ffh,    0,    0ffh,    0ffh    ;48-4f **
  133.     db    0,    0,    0,    0,    0ffh,    0,    0ffh,    0ffh    ;50-57
  134.     db    0,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0,    0    ;58-5f
  135.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;60-67
  136.  
  137. ;
  138. ; ***************************************************************************
  139. ;    These are the bios interrupt vectors
  140. ;
  141.  
  142. BIOSI8    equ    08h        ;Bios Timer interrupt
  143. BIOSI9    equ    09h        ;BIOS Hardware Keyboard interrupt
  144. BIOSIB    equ    0Bh
  145. BIOSIC    equ    0Ch
  146. BIOSI10    equ    10h
  147. BIOSI13    equ    13h        ;Bios Disk interrupt
  148. BIOSI14    equ    14h
  149. BIOSI15    equ    15h
  150. BIOSI16    equ    16h        ;Bios Keyboard interrupt
  151. BIOSI17    equ    17h
  152. BIOSI1C    equ    1Ch
  153. DOSI21    equ    21h        ;DOS service router interrupt
  154. DOSI28    equ    28h        ;DOS Idle interrupt
  155. DOSI33    equ    33h        ;mouse interrupt
  156. BIOSI1B    equ    1Bh
  157. DOSI23    equ    23h
  158. DOSI24    equ    24h
  159. ;
  160. oldint8        dd    0    ;BIOS Hardware Timer Interrupt
  161.         db    BIOSI8
  162.         dw    offset newint8    ;replacement vector
  163. oldint9        dd    0    ;BIOS Hardware Keyboard Interrupt
  164.         db    BIOSI9
  165.         dw    offset newint9    ;replacement vector
  166. oldint13    dd    0    ;BIOS Disk Interrupt
  167.         db    BIOSI13
  168.         dw    offset newint13    ;replacement vector
  169. oldint16    dd    0    ;BIOS Software Keyboard Interrupt
  170.         db    BIOSI16
  171.         dw    offset newint16    ;replacement vector
  172. oldint21    dd    0    ;DOS Services Interrupt
  173.         db    DOSI21
  174.         dw    offset newint21    ;replacement vector
  175. oldint28    dd    0    ;DOS Idle Interrupt
  176.         db    DOSI28
  177.         dw    offset newint28    ;replacement vector
  178. oldint1B    dd    0    ;control-C vector
  179.         db    BIOSI1B
  180.         dw    offset intret    ;replacement vector
  181. oldint23    dd    0    ;control-C vector
  182.         db    DOSI23
  183.         dw    offset intret    ;replacement vector
  184. oldint24    dd    0    ;critical error vector
  185.         db    DOSI24
  186.         dw    offset newint24    ;replacement vector
  187.  
  188. ;
  189. ; ***************************************************************************
  190. ;    Function to restore interrupts.
  191. ;
  192. ;    If AX=0, this function does popup-interrupts (1B, 23 and 24)
  193. ;    If AX=anything else, this function does initial interrupts
  194. ;
  195. restore_int proc near
  196.     push    ds
  197.     push    ax
  198.     push    dx
  199.     push    si
  200.     push    di
  201.     push    cx
  202.     mov    di,7            ;7 bytes per structure
  203.  
  204.     or    ax,ax
  205.     jnz    restore1
  206.  
  207.     mov    cx,3            ;three times through
  208.     mov    si,offset oldint1B    ;start at table beginning
  209.     jmp    restore_loop
  210. restore1:
  211.     mov    cx,06h            ;six interrupts!
  212.     mov    si,offset oldint8    ;top of table
  213.  
  214. restore_loop:
  215.     push    cx
  216.     push    di            ;save structure size
  217.     push    ds
  218.     mov    al,4[si]        ;interrupt number
  219.     mov    dx,[si]            ;IP of interrupt vector
  220.     mov    ds,2[si]        ;CS of interrupt vector
  221.     mov    ah,25h            ;set interrupt vector function
  222.     int    21h
  223.     pop    ds
  224.     xor    ax,ax            ;clear this entry from table
  225.     mov    [si],ax            ;so it shows not used
  226.     mov    2[si],ax
  227.     pop    di            ;get size back
  228.     add    si,di            ;do it again
  229.     pop    cx
  230.     loop    restore_loop
  231.  
  232.     pop    cx
  233.     pop    di
  234.     pop    si
  235.     pop    dx
  236.     pop    ax
  237.     pop    ds
  238.     ret
  239. restore_int endp
  240. ;
  241. ; ***************************************************************************
  242. ;    Function to set interrupts.
  243. ;
  244. ;    If AX=0, this function does popup-interrupts (1B, 23 and 24)
  245. ;    If AX=anything else, this function does initial interrupts
  246. ;
  247. setup_int proc near
  248.     push    es
  249.     push    ax
  250.     push    bx
  251.     push    dx
  252.     push    di
  253.     push    si
  254.     push    cx
  255.     mov    di,7            ;7 bytes per structure
  256.  
  257.     or    ax,ax
  258.     jnz    setup1
  259.  
  260.     mov    cx,3            ;three times through
  261.     mov    si,offset oldint1B    ;start at table beginning
  262.     jmp    setup_loop
  263. setup1:
  264.     mov    cx,06h            ;six interrupts!
  265.     mov    si,offset oldint8    ;top of table
  266.  
  267. setup_loop:
  268.     push    cx
  269.     push    di            ;save structure size
  270. ;    push    ds
  271.  
  272.     mov    ax,[si]            ;check for value
  273.     or    ax,ax            ;if not zero, next one
  274.     jnz    setup2
  275.     mov    al,4[si]        ;interrupt number
  276.     mov    ah,35h            ;get interrupt vector function
  277.  
  278.     int    21h
  279. ;    pop    ds
  280.     mov    [si],bx            ;save IP
  281.     mov    2[si],es        ;save CS
  282.  
  283.     mov    al,4[si]        ;interrupt number
  284.                     ;DS already contains correct segment
  285.     mov    dx,5[si]        ;IP of interrupt vector
  286.     mov    ah,25h            ;set interrupt vector function
  287.     int    21h
  288.  
  289. setup2:
  290.     pop    di            ;get size back
  291.     add    si,di            ;do it again
  292.     pop    cx
  293.     loop    setup_loop
  294. ex_set:
  295.     pop    cx
  296.     pop    si
  297.     pop    di
  298.     pop    dx
  299.     pop    bx
  300.     pop    ax
  301.     pop    es
  302.     ret
  303. setup_int endp
  304.  
  305. setdta    macro    ddptr
  306.     push    ds
  307.     mov    ax,1a00h    ;Function used to get current DTA address }
  308.     mov    dx,word ptr ddptr    ;Offset of DTA returned }
  309.     mov    ds,word ptr ddptr+2    ;Segment of DTA returned by DOS }
  310.     int    21h        ;Execute MSDos function request }
  311.     pop    ds
  312.     endm
  313.  
  314. getdta    macro    ddptr
  315.     push    es
  316.     mov    ax,2f00h    ;Function used to get current DTA address }
  317.     int    21h        ;Execute MSDos function request }
  318.     mov    word ptr ddptr+2,es        ;Segment of DTA returned by DOS }
  319.     mov    word ptr ddptr,bx        ;Offset of DTA returned }
  320.     pop    es
  321.     endm
  322.  
  323. ;
  324. ; ***************************************************************************
  325. ;    S E T   P S P
  326. ;
  327. ;    A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack   }
  328. ;    Then the PSP get/set functions are issued at the DOS prompt. The  }
  329. ;    following checks are made, forcing DOS to use the "critical"      }
  330. ;    stack when the TSR enters at the INDOS level.                     }
  331. ;
  332. ;        BX contains the PSP segment to set
  333. ;
  334. setpsp    proc    near
  335.     call    checkindos        ;If Version < 3, and INDOS,...
  336.     jz    do_set_psp
  337.     mov    byte ptr es:[di],0ffh    ;set critical flag
  338.  
  339.  
  340. do_set_psp:
  341.     mov    ax,5000h        ;set PSP* function
  342.                     ;BX already holds segment
  343.     int 21h                ;DOS function request
  344.  
  345.     call    checkindos        ;If Version < 3, and INDOS,...
  346.     jz    set_out
  347.     mov    byte ptr es:[di],0h    ;set critical flag
  348.  
  349. set_out:
  350.     ret
  351.  
  352. setpsp    endp
  353.  
  354. ;
  355. ; ***************************************************************************
  356. ;    G E T   P S P
  357. ;
  358. ;    A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack   }
  359. ;    Then the PSP get/set functions are issued at the DOS prompt. The  }
  360. ;    following checks are made, forcing DOS to use the "critical"      }
  361. ;    stack when the TSR enters at the INDOS level.                     }
  362. ;
  363. ;        The PSP Segment is returned in BX
  364. ;
  365. getpsp    proc    near
  366.     call    checkindos        ;If Version < 3, and INDOS,...
  367.     jz    do_get_psp
  368.     mov    byte ptr es:[di],0ffh    ;set critical flag
  369.  
  370. do_get_psp:
  371.     mov    ax,5100h        ;get PSP function
  372.     int 21h                ;DOS function request
  373.                     ;BX now holds PSP segment
  374.     call    checkindos        ;If Version < 3, and INDOS,...
  375.     jz    get_out
  376.     mov    byte ptr es:[di],0h    ;clear critical flag
  377.  
  378. get_out:
  379.     ret
  380.  
  381. getpsp    endp
  382.  
  383. ;
  384. ; ***************************************************************************
  385. ;    This routine checks the version of DOS in use, and returns the
  386. ;    ZF=0 (Not zero) if version < 3.0, and INDOS is set.  Internal function
  387. ;    is called ONLY by getpsp and setpsp.
  388. ;
  389. checkindos proc near
  390.     mov    al,dosversion
  391.     cmp    al,3            ;If Version is less than 3.0 ...
  392.     jge    ok_ret
  393.     mov    es,word ptr indosflag+2    ; ... and ...
  394.     mov    di,word ptr indosflag
  395.     mov    al,byte ptr es:[di]
  396.     or    al,al            ;INDOS is set ...
  397.     jz    ok_ret
  398.     mov    es,word ptr doscriterr+2; ... then ...
  399.     mov    di,word ptr doscriterr
  400.     jmp    fixit            ;exit with not zero
  401. ok_ret:
  402.     xor    al,al            ;set zero flag
  403. fixit:
  404.     ret
  405.  
  406. checkindos endp
  407.  
  408. ;
  409. ; ***************************************************************************
  410. ;    Internal routine to establish addressibility of the data segment
  411. ;
  412. dds proc near
  413.     push    cs
  414.     pop    ds
  415.     ret
  416. dds endp
  417.  
  418. newint9 proc far
  419.  
  420. ;NOTE:  None of this pusing/popping is really necessary for THIS particular
  421. ;TSR, so I;ve commented it out.
  422. ;
  423. ;    pushf
  424. ;    cli
  425. ;    push    ds
  426. ;    push    es
  427. ;    push    ax
  428. ;    push    bx
  429. ;    push    cx
  430. ;    push    dx
  431. ;    push    di
  432. ;    push    si
  433.  
  434. ;    push    cs
  435. ;    pop    ds
  436.  
  437. ;internal INT9 processing here
  438.  
  439. ;getkey_out:
  440. ;    pop    si
  441. ;    pop    di
  442. ;    pop    dx
  443. ;    pop    cx
  444. ;    pop    bx
  445. ;    pop    ax
  446. ;    pop    es
  447. ;    pop    ds
  448. ;out_9:
  449. ;    popf
  450.  
  451. ;    cli
  452.     jmp    dword ptr cs:oldint9
  453.  
  454.  
  455. ;
  456. ; ***************************************************************************
  457. ;    This is the int return (IRET) dummy pointer 
  458. ;
  459. intret:
  460.     iret
  461.  
  462. ;This routine will be used at some future point ... commented out for now.
  463. ;
  464. ;ignore_9:
  465. ;    in    al,60h
  466. ;    push    ax
  467. ;    pop    ax
  468. ;    in    al,61h
  469. ;    mov    ah,al
  470. ;    or    al,80h
  471. ;    out    61h,al
  472. ;    xchg    ah,al
  473. ;    out    61h,al
  474. ;    mov    al,20h
  475. ;    out    20h,al
  476. ;    pop    ax
  477. ;    jmp    short out_9
  478.  
  479. newint9 endp
  480.  
  481. new_bios_flag    db    0
  482.  
  483. newint16 proc far
  484. start_again:
  485.     cmp    ah,00        ;if char request,
  486.     je    func00        ;loop for character
  487.     cmp    ah,01        ;if character availability test
  488.     je    func01        ;go check for char
  489.     push    ax
  490.     and    ah,10h        ;test for extra bios
  491.     pop    ax
  492.     jz    gobios16
  493.     mov    cs:new_bios_flag,10h
  494.     and    ah,3
  495.     jmp    short start_again
  496. gobios16:
  497.     or    ah,cs:new_bios_flag
  498.     jmp    dword ptr cs:[oldint16]
  499.                 ;go to bios interrupt 16
  500. func01:
  501.     push    ds
  502.     push    si
  503.     push    di
  504.     call    dds
  505. func01A:
  506.     call    keystat        ;look at key buffer
  507.     pushf            ;save return flags
  508.     jz    fret01        ;return if no key
  509.     call    hotcheck    ;test for hot key
  510.     jnz    fret01        ;not there
  511.     popf            ;flags back for loop
  512.     jmp    func01A        ;it was a hot key -- skip it!
  513. fret01:
  514.     mov    new_bios_flag,0
  515.     popf            ;flags back
  516.     pop    di
  517.     pop    si
  518.     pop    ds
  519.     ret    2        ;return to user
  520. func00:
  521.     push    ds
  522.     push    si
  523.     push    di
  524.     call    dds
  525. func00A:
  526.     call    keystat        ;wait until character available
  527.     jz    func00A        ;loop it
  528.     call    hotcheck    ;test for a hot key
  529.     jz    func00A        ;it sure was -- start loop again
  530.  
  531. b_func0:
  532.     mov    ah,0        ;get the next user key
  533.     or    ah,new_bios_flag
  534.     pushf            ;simulate it ...
  535.     call    dword ptr cs:oldint16    ;do bios
  536.  
  537. func_out:
  538.     pushf            ;save return flags
  539.     jmp    fret01        ;restore and return
  540.  
  541. newint16 endp
  542.  
  543. ;
  544. ;       call the background task if no key is available
  545. ;
  546. keystat proc near
  547.                 ;preserves all registers except
  548.                 ;AX,SI,DI,DS & flags
  549.     push    cx
  550.     push    bx
  551.  
  552.     push    ax
  553.     push    ds
  554.     cld
  555.  
  556.     mov    ax,BIOS_DATA
  557.     mov    ds,ax        ;set data segment
  558.     mov    si,KB_FLG1    ;set source
  559.  
  560.     lodsw            ;get the high and low bytes
  561.     pop    ds
  562.     mov    bx,ax        ;save it away
  563.     pop    ax
  564.  
  565.  
  566.     mov    al,SHIFTST    ;shift state check
  567.  
  568.     or    al,al        ;is it zero?
  569.     jz    sim_16        ;yes -- set flag for simulation
  570.  
  571.     push    bx        ;save current flags
  572.     and    bl,al        ;mask bits
  573.     cmp    al,bl        ;are we the same?
  574.     pop    bx
  575.     jne    skip_16A    ;nope -- next one
  576. sim_16:
  577.     or    word ptr STATUS,SHIFTSON    ;set the flag
  578.  
  579.     mov    al,HOTKEY    ;is this control block the last one?
  580.  
  581.     or    al,al
  582.     jnz    short skip_16
  583.     or    word ptr STATUS,HOTKEYON    ;turn on hotkey flag
  584.     jmp    short skip_16
  585. skip_16A:
  586.     and    word ptr STATUS,NOT SHIFTSON    ;set the flag
  587.  
  588. skip_16:
  589.     mov    ah,1            ;see if character is available
  590.     or    ah,new_bios_flag
  591.     pushf                ;simulate interrupt
  592.     call    dword ptr cs:oldint16
  593.  
  594.     jnz    exit_161        ;got one
  595.  
  596. chkdos_161:
  597.     cld
  598.     push    ds            ;check if dos critical error in effect
  599.     push    si
  600.         lds     si,cs:doscriterr    ;zero says dos is interruptable
  601.     lodsb                ;$ff  says dos is in a critical state
  602.     lds     si,cs:indosflag        ;if indos then int $28 issued by dos
  603.     or    al,[si]            ;so we dont have to.
  604.                     ;account for active interrupts
  605.     or    al,cs:intflags        ;any flags says we dont issue call
  606.     pop    si            ;to the background.
  607.     pop    ds
  608.     cmp    al,01            ;must be indos flag only
  609.     jg    skip28            ;dos cannot take an interrupt yet
  610.     int    28h            ;call dos idle function (background dispatch).
  611. skip28:
  612.     xor    ax,ax            ;show  no keycode available
  613. exit_161:
  614.     pop    bx
  615.     pop    cx
  616.         ret
  617.  
  618. keystat    endp
  619.  
  620. hotcheck proc near    ;DI points to TSR Block character is from
  621.             ;SI holds ID of TSR routine
  622.  
  623.     push    cx
  624.     push    ax        ;save character
  625.     push    di
  626.  
  627.     mov    al,HOTKEY
  628.  
  629.     or    al,al
  630.     jz    only_shift    ;special case for shift-state only
  631.  
  632.     cmp    al,ah        ;do scan codes match?
  633.     jne    again_16H
  634.  
  635. only_shift:
  636.     push    ax
  637.     mov    ax,STATUS    ;get TSR status flags
  638.     test    ax,SHIFTSON    ;are shift codes set?
  639.     jz    again_16H1    ;nope -- try next one
  640.     
  641.     or    ax,HOTKEYON    ;turn on hotkey flag
  642.     mov    STATUS,ax    ;put new flags back
  643.  
  644.     pop    ax
  645.     or    al,al        ;test for special case
  646.     pop    di        ;get back Block pointer
  647.     pop    ax        ;throwing this away
  648.  
  649.     jz    hot_out        ;special case for flags only
  650.  
  651. b_func1:
  652.     mov    ah,0        ;get rid of the character
  653.     or    ah,new_bios_flag
  654.     pushf            ;simulate interrupt
  655.     call    dword ptr cs:oldint16    ;do bios
  656.  
  657. hot_out:
  658.     xor    ax,ax        ;set zero flag
  659.     jmp    got_hot
  660.  
  661. again_16H1:
  662.     pop    ax        ;get rid of flag
  663. again_16H:
  664.     mov    al,0ffh
  665.     or    al,al        ;clear zero flag if set
  666.     pop    di
  667.     pop    ax        ;get back keystroke
  668. got_hot:
  669.     pop    cx        ;get back cx register
  670.     ret
  671.  
  672. hotcheck endp
  673.  
  674. newint8 proc far
  675.  
  676.     pushf
  677.     push    ds
  678.     push    di
  679.     push    cx            ;save regs
  680.     push    ax
  681.     call    dds
  682.  
  683.     mov    ax,STATUS
  684.     test    ax,HOTKEYON    ;is hot-key flag set?
  685.     jz    again_8_2
  686.     test    ax,TSRACTIVE    ;are we already running?
  687.     jnz    again_8_2    ;yep -- next one
  688.  
  689. wait_8:
  690.     cmp    waitcount,0    ;if waiting, check time
  691.     jnz    wait2_8        ;decrement count
  692.  
  693. chkdos_8:
  694.     cld
  695.     push    ds            ;check if dos critical error in effect
  696.     push    si
  697.         lds     si,cs:doscriterr    ;zero says dos is interruptable
  698.     lodsb                ;$ff  says dos is in a critical state
  699.     lds     si,cs:indosflag        ;add in second status byte
  700.     or    al,[si]
  701.     or    al,cs:intflags        ;any flags says we're busy
  702.     pop    si
  703.     pop    ds
  704.     jnz    wait1_8            ;we're busy -- set count & EXIT
  705.     call    dopopup            ;call the popup stuff
  706.     jmp    end_8            ;and out we go
  707. wait1_8:
  708.     mov    waitcount,10h        ;set the count
  709. wait2_8:
  710.     dec    waitcount
  711.     jz    chkdos_8
  712.  
  713. again_8_2:
  714. end_8:
  715.     pushf                ;simulate interrupt ...
  716.     call dword ptr oldint8        ;do the original
  717.  
  718.     pop    ax        ;get back registers
  719.     pop    cx
  720.     pop    di
  721.     pop    ds
  722.     popf
  723.  
  724. exit_8:
  725.     iret
  726.  
  727. newint8 endp
  728.  
  729. newint28 proc far
  730.  
  731.     pushf                ;simulate interrupt ...
  732.     call dword ptr cs:oldint28    ;do the original
  733.  
  734.     push    ds
  735.     push    di
  736.     push    cx            ;save regs
  737.     push    ax
  738.  
  739.     push    cs
  740.     pop    ds
  741.  
  742. again_28:
  743.     mov    ax,STATUS    ;get TSR status flags
  744.     test    ax,HOTKEYON    ;is hot-key flag set?
  745.     jz    again_28_2
  746.     test    ax,TSRACTIVE    ;are we already running?
  747.     jnz    again_28_2    ;yep -- next one
  748.  
  749. chkdos_28:
  750.     cld
  751.     push    ds            ;check if dos critical error in effect
  752.     push    si
  753.         lds     si,cs:doscriterr    ;zero says dos is interruptable
  754.     lodsb                ;$ff  says dos is in a critical state
  755.     or    al,cs:intflags        ;any flags says we're busy
  756.                     ;NOTE:  Indos flag is always set here!
  757.                     ; ... so don't have to check it!
  758.     pop    si
  759.     pop    ds
  760.     jnz    end_28            ;we're busy-EXIT & try again next time
  761.  
  762.     mov    waitcount,0        ;clear waiting count and ...    
  763.     call    dopopup            ;... call the popup stuff
  764.     jmp    end_28            ;and out we go
  765.  
  766. again_28_2:
  767. end_28:
  768.     pop    ax        ;get back registers
  769.     pop    cx
  770.     pop    di
  771.     pop    ds
  772.  
  773. exit_28:
  774.     iret
  775.  
  776. newint28 endp
  777.  
  778. di28    dw    0
  779. ds28    dw    0
  780.  
  781. dopopup proc near
  782.  
  783.     cli            ;no interrupts now!!!
  784.     
  785.     or    word ptr STATUS,TSRACTIVE    ;set active bit
  786.  
  787.     mov    ISSEG,ss        ;save user's stack segment
  788.     mov    ISPTR,sp        ;... and stack pointer
  789.                     ;DS was saved in INT8 or INT28
  790.  
  791.     mov    ss,SSEG        ;load TSR stack segment ...
  792.     mov    sp,SPTR        ;... and stack pointer
  793.  
  794.     push    bp            ;save all registers except for
  795.     push    bx            ;...those saved by calling routine,
  796.     push    dx            ;...DS,DI,CX,AX are restored on exit
  797.     push    si            ;...from this procedure
  798.     push    es
  799.  
  800.     mov    [di28],di        ;save TSR pointer until after saving
  801.     mov    [ds28],ds        ;...Indos stack
  802.  
  803.     mov    di,sp            ;destination is our stack
  804.     dec    di
  805.     dec    di            ;back off current word
  806.     mov    ax,ss            ;move stack segment
  807.     mov    es,ax            ;...to extra segment
  808.  
  809.     mov    si,cs:dossptr        ;source is DOS Indos Primary Stack
  810.     mov    ds,cs:dossseg        ;...and segment, of course
  811.     dec    si            ;point to last word on DOS stack
  812.     dec    si
  813.  
  814.     mov    cx,40h            ;stack size to save
  815.  
  816.     mov    ax,sp            ;save current stack pointer ...
  817.     sub    ax,cx            ;...and make room to avoid
  818.     sub    ax,cx            ;...overwriting during REP
  819.     mov    sp,ax            ;...and put it back
  820.  
  821.     std                ;set the direction flag ...
  822.     rep    movsw            ;and move the 40 word stack
  823.     mov    sp,di
  824.     cld                ;direction flag back
  825.  
  826.     mov    di,cs:di28        ;get TSR pointer back
  827.     mov    ds,cs:ds28
  828.  
  829.     sti                ;interrupts back on!!
  830.  
  831.     getdta    OLDDTA            ;save current DTA
  832.  
  833.     call    getpsp            ;get current PSP segment
  834.     mov    OLDPSP,bx        ;...returned in BX
  835.  
  836.     mov    bx,PSP            ;set TSR's PSP in BX
  837.     call    setpsp            ;...and do it
  838.  
  839.     setdta    DTA
  840.  
  841.     xor    ax,ax
  842.     call    setup_int        ;replace with our handler
  843.  
  844.     push    ds            ;save DS:DI for after popup
  845.     push    di
  846.     mov    ds,DSEG        ;load user's data segment
  847.  
  848.  
  849.     call    dword ptr cs:POPUP    ;call the user routine!!
  850.  
  851.  
  852.     pop    di            ;restore ds:di for addressibility
  853.     pop    ds
  854.  
  855.     mov    bx,OLDPSP        ;replace interrupted PSP
  856.     call    setpsp
  857.  
  858.     setdta    OLDDTA        ;restore interrupted DTA
  859.  
  860.     xor    ax,ax
  861.     call    restore_int        ;restore interrupts as well
  862.  
  863. ;terminate checking here!
  864.  
  865.     cli                ;no interrupts while manipulating stack
  866.  
  867.     mov    cx,40h            ;stack size here
  868.  
  869.     mov    di28,di            ;save TSR pointer until after saving
  870.     mov    ds28,ds            ;...Indos stack
  871.  
  872.     mov    ax,ss            ;source is current stack
  873.     mov    ds,ax
  874.     mov    si,sp            ;point to last word on our stack
  875.     inc    si
  876.     inc    si
  877.  
  878.     mov    es,cs:dossseg        ;destination is dos stack
  879.     mov    di,cs:dossptr
  880.     sub    di,cx            ;point backward to last used word 
  881.     sub    di,cx            ;...on dos stack
  882.     cld
  883.     rep    movsw
  884.  
  885.     mov    sp,si            ;skip over moved words
  886.  
  887.     mov    di,cs:[di28]        ;get TSR pointer back
  888.     mov    ds,cs:[ds28]
  889.  
  890.     pop    es
  891.     pop    si
  892.     pop    dx
  893.     pop    bx
  894.     pop    bp
  895.  
  896. ;
  897. ; ***************************************************************************
  898. ;
  899. ;    CAUTION!!!! THE USER ROUTINE MUST RETURN SS:SP WITH THE SAME
  900. ;        VALUES AS ON ENTRY.  OTHER REGISTERS WILL BE RESTORED
  901. ;        BY THIS ROUTINE.
  902. ;
  903. ; ***************************************************************************
  904. ;
  905. back_popup:
  906.  
  907.     mov    ss,ISSEG        ;restore user's stack segment
  908.     mov    sp,ISPTR        ;... and stack pointer
  909.                     ;DS will be restored in INT8 or INT28
  910.     and    STATUS,NOT HOTKEYON+TSRACTIVE
  911.                     ;clear hot-key and inuse bits
  912.  
  913.     sti                ;enable interrupts again
  914. dopopup_end:
  915.     ret
  916.  
  917. dopopup endp
  918.  
  919. newint13 proc far
  920.  
  921.     pushf
  922.     or     cs:intflags,ININT13
  923.     popf            ;get back original flags
  924.  
  925.     pushf                ;simulate interrupt ...
  926.     call dword ptr cs:oldint13    ;do the original
  927.  
  928.     pushf
  929.     and    cs:intflags,NOT ININT13
  930.     popf
  931.  
  932.     ret    2
  933.  
  934. newint13 endp
  935.  
  936. newint21 proc far
  937.     pushf
  938.     sti                ;allow interrupts
  939.  
  940.     cmp    ah,63h            ;max number
  941.     jg    notme
  942.     push    ax            ;some INT21 functions must be left
  943.     push    bx            ;...alone.  They either never return,
  944.     mov    bx,offset dos21funcs    ;...grab parameters from the stack
  945.     mov    al,ah            ;...or can be interrupted.
  946.     xlat    cs:dos21funcs        ;test function against table
  947.     or    al,al            ;wait for functions marked zero
  948.     pop    bx            ;these ,ust be left alone
  949.     pop    ax
  950.     jz    notme            ;jump to original INT21
  951.     jmp    short set_21
  952. notme:
  953.     popf
  954.     jmp    dword ptr cs:oldint21
  955.  
  956. set_21:
  957.     or     cs:intflags,ININT21    ;say int21 is active
  958.     popf                ;get original flags back
  959.     pushf                ;save flags and all regs that
  960.  
  961.     call    dword ptr cs:oldint21    ;do the interrupt
  962.     sti
  963.  
  964.     pushf                ;save return registers
  965.     and    cs:intflags,NOT ININT21    ;not active any more
  966.     popf                ;they're back
  967.  
  968.     ret    2            ;return with flags set
  969.  
  970. newint21 endp
  971.  
  972. error24    dw    0
  973.  
  974. newint24 proc far
  975.     pushf
  976.     mov    cs:error24,di        ;save the error
  977.     mov    al,0
  978.     iret
  979. newint24 endp
  980.  
  981. pop_routine proc far    ;just prints a msg and gets out
  982.     lea    dx,hello
  983.     mov    ah,9
  984.     int    21h        ; display copyright notice
  985.     ret
  986. pop_routine endp
  987.  
  988.  
  989. TSR_stack     db    512 dup(0)
  990. TSR_stack_end:
  991.  
  992. res_part:    ; this is the end of my resident code so I can tell DOS
  993.         ; how big I am
  994. ;
  995. ;
  996. ; ***************************************************************************
  997. ;
  998. ;    Initialization Code -- load routine and point new int14
  999. ;
  1000. second_time    db    0    ;flag used to test for odd-byte check
  1001.  
  1002. begin:
  1003.     lea    dx,cpyrt
  1004.     mov    ah,9
  1005.     int    21h        ; display copyright notice
  1006.  
  1007.     call    getpsp        ;Current PSP returned in BX
  1008.     mov    PSP,bx
  1009.     getdta    DTA        ;macro to get Disk Transfer Address
  1010.  
  1011.     mov    ax,ds
  1012.     mov    DSEG,ax        ;save it
  1013.     mov    SSEG,ax        ;save it here too!
  1014.     mov    word ptr POPUP+2,ax    ;save it here three!
  1015.     mov    ax,offset TSR_stack_end
  1016.     sub    ax,2
  1017.     mov    SPTR,ax
  1018.  
  1019.     mov    SHIFTST,08h    ;ALT-Key + ...
  1020.     mov    HOTKEY,2dh    ;...'X' to pop up
  1021.  
  1022.     mov    word ptr POPUP,offset pop_routine
  1023.  
  1024.     mov    ax,3400h        ;find the DOS Indos Byte
  1025.     int    21h
  1026. ;IF SOMEBODY HAS AT&T DOS 2.11, would you please try something?
  1027. ;
  1028. ;Move the label 'crit2' to the second location (where it is currently)
  1029. ;commented out, and see how it works.  There is still some dissension
  1030. ;about where the critical byte actually is in that version of DOS!
  1031.  
  1032. crit2:
  1033.     mov    word ptr indosflag,bx    ;offset returned in BX
  1034.     mov    word ptr indosflag+2,es    ;segment returned in ES
  1035.     mov    dossseg,es        ;this is also the DOS stack segment
  1036.     mov    word ptr doscriterr+2,es;as well as the segment for the critical flag
  1037. ;crit2:
  1038.     mov    cx,2000h         ;search for instruction: CMP [crit flag],00
  1039.     mov    ax,3e80h         ;3e80 is opcode to search for
  1040.     mov    di,bx             ;start search here
  1041. crit3:
  1042.     repnz    scasw             ;search until a match is found
  1043.     jnz    no_crit_found         ;couldn't find critical byte
  1044.                     ;we're going to search for the
  1045.                     ;address of the critical flag
  1046.                     ;es:[di-2] now points to:
  1047.                     ;       CMP [crit flag],00
  1048.                     ;       JNZ ...
  1049.                     ;       MOV SP,indos stack address      
  1050.     mov    al,byte ptr es:[di+5]     ;MOV SP,xxxx
  1051.     cmp    al,0bch         ;opcode for MOV SP
  1052.     jne    crit3             ;bad place.  Sorry
  1053.     mov    ax,word ptr es:[di]     ;here's the critical byte address
  1054.     mov    word ptr doscriterr,ax
  1055.     mov    ax,word ptr es:[di+6]     ;...and here's the stack pointer
  1056.     mov    word ptr dossptr,ax
  1057.     mov    ax,1                                                     
  1058.     jmp    no_error
  1059. no_crit_found:
  1060.     mov    al,second_time        ;did we already try odd bytes?
  1061.     or    al,al
  1062.     jz    crit_again        ;nope ...
  1063.     jmp    bad_dos_version        ;we tried!
  1064. crit_again:
  1065.     mov    second_time,1        ;set the flag
  1066.     inc    bx            ;increment pointer (needed for AT&T
  1067.     jmp    crit2            ;DOS 2.11 and some others
  1068.  
  1069. no_error:
  1070.     mov    ax,1
  1071.     call    setup_int
  1072.  
  1073.     lea    dx,res_part    ; load offset of top of code
  1074.     add    dx,15        ;round up
  1075.     shr    dx,1
  1076.     shr    dx,1
  1077.     shr    dx,1
  1078.     shr    dx,1        ; divide by 16 to get paragraphs
  1079.     xor    al,al        ; no errors
  1080.     mov    ah,31h        ; load terminate & stay resident code
  1081.     int    21h
  1082.  
  1083. bad_dos_version:
  1084.     lea    dx,bad_dos_msg
  1085.     mov    ah,9
  1086.     int    21h        ; display error message
  1087.     mov    ax,4cffh            ;exit with -1 return code
  1088.     int    21h
  1089.  
  1090. code    ends
  1091.  
  1092.     end    start
  1093.